package com.ziven.learn.view

import android.animation.ValueAnimator
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import androidx.core.animation.addListener

/**
 * @author zhiyuan.zhou
 * @date 2024/12/9
 */
class SortDrawView
    @JvmOverloads
    constructor(
        context: Context,
        attrs: AttributeSet? = null,
        defStyleAttr: Int = 0,
    ) : View(context, attrs, defStyleAttr) {
        private val paint: Paint = Paint()
        private val dataRect = mutableListOf<RectF>()
        private var originData: IntArray? = null
        private var sortData: IntArray? = null

        private val heightMax = 1200F
        private val width = 64F
        private val height = 64F
        private val widthMax: Float
        private val radius = 16F

        private val changStep = mutableListOf<Pair<Int, Int>>()
        private var isDoing = false
        private var animIndex = 0
        private val animMax = 100F

        init {
            paint.color = Color.RED
            paint.style = Paint.Style.FILL
            widthMax = resources.displayMetrics.widthPixels.toFloat()
        }

        fun isDoing() = isDoing

        fun getData() = sortData ?: IntArray(0)

        fun putData(data: IntArray) {
            isDoing = true
            dataRect.clear()
            originData = IntArray(data.size)
            sortData = IntArray(data.size)
            val padding = (widthMax - width * data.size) / (data.size + 1)
            data.forEachIndexed { index, it ->
                originData!![index] = it
                sortData!![index] = it
                val left = (padding + width) * index + padding
                val top = heightMax - it * height
                val right = left + width
                val bottom = heightMax
                val rectF = RectF(left, top, right, bottom)
                dataRect.add(rectF)
            }
            postInvalidate()
        }

        fun getText(): String {
            var originStr = "Origin:"
            originData!!.forEach {
                originStr += "$it "
            }
            var sortedStr = "Sorted:"
            sortData!!.forEach {
                sortedStr += "$it "
            }
            return """
                $originStr
                $sortedStr
                "Swap Count: ${changStep.size}"
                """.trimIndent()
        }

        fun swap(
            index1: Int,
            index2: Int,
        ) {
            val data1 = sortData!![index1]
            val data2 = sortData!![index2]
            sortData!![index1] = data2
            sortData!![index2] = data1
            changStep.add(Pair(index1, index2))
        }

        fun drawAnim(isStart: Boolean) {
            if (isStart) animIndex = 0 else animIndex++
            if (animIndex >= changStep.size) {
                isDoing = false
                return
            }
            val pair = changStep[animIndex]
            drawAnimInner(dataRect[pair.first], dataRect[pair.second])
        }

        private fun drawAnimInner(
            rectF1: RectF,
            rectF2: RectF,
        ) {
            val height1 = rectF1.bottom - rectF1.top
            val height2 = rectF2.bottom - rectF2.top
            val animator1 = ValueAnimator.ofFloat(animMax, 0F)
            val animator2 = ValueAnimator.ofFloat(0F, animMax)

            animator1.addUpdateListener {
                val value = it.animatedValue as Float
                rectF1.top = heightMax - value * height1 / animMax
                rectF2.top = heightMax - value * height2 / animMax
                postInvalidate()
            }
            animator1.duration = 1000
            animator1.addListener(onEnd = {
                animator2.start()
            })
            animator1.start()

            animator2.addUpdateListener {
                val value = it.animatedValue as Float
                rectF1.top = heightMax - value * height2 / animMax
                rectF2.top = heightMax - value * height1 / animMax
                postInvalidate()
            }
            animator2.duration = 1000
            animator2.addListener(onEnd = {
                drawAnim(false)
            })
        }

        override fun onDraw(canvas: Canvas) {
            dataRect.forEachIndexed { _, it ->
                canvas.drawRoundRect(it, radius, radius, paint)
            }
        }
    }
